package com.baomidou.kisso.common.bcprov.util;

import java.math.BigInteger;

/**
 * General array utilities.
 */
public final class Arrays {

	private Arrays() {
		// static class, hide constructor
	}


	public static boolean areEqual( boolean[] a, boolean[] b ) {
		if ( a == b ) {
			return true;
		}

		if ( a == null || b == null ) {
			return false;
		}

		if ( a.length != b.length ) {
			return false;
		}

		for ( int i = 0 ; i != a.length ; i++ ) {
			if ( a[i] != b[i] ) {
				return false;
			}
		}

		return true;
	}


	public static boolean areEqual( char[] a, char[] b ) {
		if ( a == b ) {
			return true;
		}

		if ( a == null || b == null ) {
			return false;
		}

		if ( a.length != b.length ) {
			return false;
		}

		for ( int i = 0 ; i != a.length ; i++ ) {
			if ( a[i] != b[i] ) {
				return false;
			}
		}

		return true;
	}


	public static boolean areEqual( byte[] a, byte[] b ) {
		if ( a == b ) {
			return true;
		}

		if ( a == null || b == null ) {
			return false;
		}

		if ( a.length != b.length ) {
			return false;
		}

		for ( int i = 0 ; i != a.length ; i++ ) {
			if ( a[i] != b[i] ) {
				return false;
			}
		}

		return true;
	}


	/**
	 * A constant time equals comparison - does not terminate early if
	 * test will fail.
	 *
	 * @param a first array
	 * @param b second array
	 * @return true if arrays equal, false otherwise.
	 */
	public static boolean constantTimeAreEqual( byte[] a, byte[] b ) {
		if ( a == b ) {
			return true;
		}

		if ( a == null || b == null ) {
			return false;
		}

		if ( a.length != b.length ) {
			return false;
		}

		int nonEqual = 0;

		for ( int i = 0 ; i != a.length ; i++ ) {
			nonEqual |= (a[i] ^ b[i]);
		}

		return nonEqual == 0;
	}


	public static boolean areEqual( int[] a, int[] b ) {
		if ( a == b ) {
			return true;
		}

		if ( a == null || b == null ) {
			return false;
		}

		if ( a.length != b.length ) {
			return false;
		}

		for ( int i = 0 ; i != a.length ; i++ ) {
			if ( a[i] != b[i] ) {
				return false;
			}
		}

		return true;
	}


	public static boolean areEqual( long[] a, long[] b ) {
		if ( a == b ) {
			return true;
		}

		if ( a == null || b == null ) {
			return false;
		}

		if ( a.length != b.length ) {
			return false;
		}

		for ( int i = 0 ; i != a.length ; i++ ) {
			if ( a[i] != b[i] ) {
				return false;
			}
		}

		return true;
	}


	public static boolean areEqual( Object[] a, Object[] b ) {
		if ( a == b ) {
			return true;
		}
		if ( a == null || b == null ) {
			return false;
		}
		if ( a.length != b.length ) {
			return false;
		}
		for ( int i = 0 ; i != a.length ; i++ ) {
			Object objA = a[i], objB = b[i];
			if ( objA == null ) {
				if ( objB != null ) {
					return false;
				}
			} else if ( !objA.equals(objB) ) {
				return false;
			}
		}
		return true;
	}


	public static boolean contains( short[] a, short n ) {
		for ( int i = 0 ; i < a.length ; ++i ) {
			if ( a[i] == n ) {
				return true;
			}
		}
		return false;
	}


	public static boolean contains( int[] a, int n ) {
		for ( int i = 0 ; i < a.length ; ++i ) {
			if ( a[i] == n ) {
				return true;
			}
		}
		return false;
	}


	public static void fill( byte[] array, byte value ) {
		for ( int i = 0 ; i < array.length ; i++ ) {
			array[i] = value;
		}
	}


	public static void fill( char[] array, char value ) {
		for ( int i = 0 ; i < array.length ; i++ ) {
			array[i] = value;
		}
	}


	public static void fill( long[] array, long value ) {
		for ( int i = 0 ; i < array.length ; i++ ) {
			array[i] = value;
		}
	}


	public static void fill( short[] array, short value ) {
		for ( int i = 0 ; i < array.length ; i++ ) {
			array[i] = value;
		}
	}


	public static void fill( int[] array, int value ) {
		for ( int i = 0 ; i < array.length ; i++ ) {
			array[i] = value;
		}
	}


	public static int hashCode( byte[] data ) {
		if ( data == null ) {
			return 0;
		}

		int i = data.length;
		int hc = i + 1;

		while ( --i >= 0 ) {
			hc *= 257;
			hc ^= data[i];
		}

		return hc;
	}


	public static int hashCode( char[] data ) {
		if ( data == null ) {
			return 0;
		}

		int i = data.length;
		int hc = i + 1;

		while ( --i >= 0 ) {
			hc *= 257;
			hc ^= data[i];
		}

		return hc;
	}


	public static int hashCode( int[][] ints ) {
		int hc = 0;

		for ( int i = 0 ; i != ints.length ; i++ ) {
			hc = hc * 257 + hashCode(ints[i]);
		}

		return hc;
	}


	public static int hashCode( int[] data ) {
		if ( data == null ) {
			return 0;
		}

		int i = data.length;
		int hc = i + 1;

		while ( --i >= 0 ) {
			hc *= 257;
			hc ^= data[i];
		}

		return hc;
	}


	public static int hashCode( short[][][] shorts ) {
		int hc = 0;

		for ( int i = 0 ; i != shorts.length ; i++ ) {
			hc = hc * 257 + hashCode(shorts[i]);
		}

		return hc;
	}


	public static int hashCode( short[][] shorts ) {
		int hc = 0;

		for ( int i = 0 ; i != shorts.length ; i++ ) {
			hc = hc * 257 + hashCode(shorts[i]);
		}

		return hc;
	}


	public static int hashCode( short[] data ) {
		if ( data == null ) {
			return 0;
		}

		int i = data.length;
		int hc = i + 1;

		while ( --i >= 0 ) {
			hc *= 257;
			hc ^= (data[i] & 0xff);
		}

		return hc;
	}


	public static int hashCode( Object[] data ) {
		if ( data == null ) {
			return 0;
		}

		int i = data.length;
		int hc = i + 1;

		while ( --i >= 0 ) {
			hc *= 257;
			hc ^= data[i].hashCode();
		}

		return hc;
	}


	public static byte[] clone( byte[] data ) {
		if ( data == null ) {
			return null;
		}
		byte[] copy = new byte[data.length];

		System.arraycopy(data, 0, copy, 0, data.length);

		return copy;
	}


	public static byte[] clone( byte[] data, byte[] existing ) {
		if ( data == null ) {
			return null;
		}
		if ( (existing == null) || (existing.length != data.length) ) {
			return clone(data);
		}
		System.arraycopy(data, 0, existing, 0, existing.length);
		return existing;
	}


	public static byte[][] clone( byte[][] data ) {
		if ( data == null ) {
			return null;
		}

		byte[][] copy = new byte[data.length][ ];

		for ( int i = 0 ; i != copy.length ; i++ ) {
			copy[i] = clone(data[i]);
		}

		return copy;
	}


	public static byte[][][] clone( byte[][][] data ) {
		if ( data == null ) {
			return null;
		}

		byte[][][] copy = new byte[data.length][ ][ ];

		for ( int i = 0 ; i != copy.length ; i++ ) {
			copy[i] = clone(data[i]);
		}

		return copy;
	}


	public static int[] clone( int[] data ) {
		if ( data == null ) {
			return null;
		}
		int[] copy = new int[data.length];

		System.arraycopy(data, 0, copy, 0, data.length);

		return copy;
	}


	public static long[] clone( long[] data ) {
		if ( data == null ) {
			return null;
		}
		long[] copy = new long[data.length];

		System.arraycopy(data, 0, copy, 0, data.length);

		return copy;
	}


	public static long[] clone( long[] data, long[] existing ) {
		if ( data == null ) {
			return null;
		}
		if ( (existing == null) || (existing.length != data.length) ) {
			return clone(data);
		}
		System.arraycopy(data, 0, existing, 0, existing.length);
		return existing;
	}


	public static short[] clone( short[] data ) {
		if ( data == null ) {
			return null;
		}
		short[] copy = new short[data.length];

		System.arraycopy(data, 0, copy, 0, data.length);

		return copy;
	}


	public static BigInteger[] clone( BigInteger[] data ) {
		if ( data == null ) {
			return null;
		}
		BigInteger[] copy = new BigInteger[data.length];

		System.arraycopy(data, 0, copy, 0, data.length);

		return copy;
	}


	public static byte[] copyOf( byte[] data, int newLength ) {
		byte[] tmp = new byte[newLength];

		if ( newLength < data.length ) {
			System.arraycopy(data, 0, tmp, 0, newLength);
		} else {
			System.arraycopy(data, 0, tmp, 0, data.length);
		}

		return tmp;
	}


	public static char[] copyOf( char[] data, int newLength ) {
		char[] tmp = new char[newLength];

		if ( newLength < data.length ) {
			System.arraycopy(data, 0, tmp, 0, newLength);
		} else {
			System.arraycopy(data, 0, tmp, 0, data.length);
		}

		return tmp;
	}


	public static int[] copyOf( int[] data, int newLength ) {
		int[] tmp = new int[newLength];

		if ( newLength < data.length ) {
			System.arraycopy(data, 0, tmp, 0, newLength);
		} else {
			System.arraycopy(data, 0, tmp, 0, data.length);
		}

		return tmp;
	}


	public static long[] copyOf( long[] data, int newLength ) {
		long[] tmp = new long[newLength];

		if ( newLength < data.length ) {
			System.arraycopy(data, 0, tmp, 0, newLength);
		} else {
			System.arraycopy(data, 0, tmp, 0, data.length);
		}

		return tmp;
	}


	public static BigInteger[] copyOf( BigInteger[] data, int newLength ) {
		BigInteger[] tmp = new BigInteger[newLength];

		if ( newLength < data.length ) {
			System.arraycopy(data, 0, tmp, 0, newLength);
		} else {
			System.arraycopy(data, 0, tmp, 0, data.length);
		}

		return tmp;
	}


	/**
	 * Make a copy of a range of bytes from the passed in data array. The range can
	 * extend beyond the end of the input array, in which case the return array will
	 * be padded with zeroes.
	 *
	 * @param data the array from which the data is to be copied.
	 * @param from the start index at which the copying should take place.
	 * @param to the final index of the range (exclusive).
	 *
	 * @return a new byte array containing the range given.
	 */
	public static byte[] copyOfRange( byte[] data, int from, int to ) {
		int newLength = getLength(from, to);

		byte[] tmp = new byte[newLength];

		if ( data.length - from < newLength ) {
			System.arraycopy(data, from, tmp, 0, data.length - from);
		} else {
			System.arraycopy(data, from, tmp, 0, newLength);
		}

		return tmp;
	}


	public static int[] copyOfRange( int[] data, int from, int to ) {
		int newLength = getLength(from, to);

		int[] tmp = new int[newLength];

		if ( data.length - from < newLength ) {
			System.arraycopy(data, from, tmp, 0, data.length - from);
		} else {
			System.arraycopy(data, from, tmp, 0, newLength);
		}

		return tmp;
	}


	public static long[] copyOfRange( long[] data, int from, int to ) {
		int newLength = getLength(from, to);

		long[] tmp = new long[newLength];

		if ( data.length - from < newLength ) {
			System.arraycopy(data, from, tmp, 0, data.length - from);
		} else {
			System.arraycopy(data, from, tmp, 0, newLength);
		}

		return tmp;
	}


	public static BigInteger[] copyOfRange( BigInteger[] data, int from, int to ) {
		int newLength = getLength(from, to);

		BigInteger[] tmp = new BigInteger[newLength];

		if ( data.length - from < newLength ) {
			System.arraycopy(data, from, tmp, 0, data.length - from);
		} else {
			System.arraycopy(data, from, tmp, 0, newLength);
		}

		return tmp;
	}


	private static int getLength( int from, int to ) {
		int newLength = to - from;
		if ( newLength < 0 ) {
			StringBuffer sb = new StringBuffer(from);
			sb.append(" > ").append(to);
			throw new IllegalArgumentException(sb.toString());
		}
		return newLength;
	}


	public static byte[] append( byte[] a, byte b ) {
		if ( a == null ) {
			return new byte[ ] { b };
		}

		int length = a.length;
		byte[] result = new byte[length + 1];
		System.arraycopy(a, 0, result, 0, length);
		result[length] = b;
		return result;
	}


	public static int[] append( int[] a, int b ) {
		if ( a == null ) {
			return new int[ ] { b };
		}

		int length = a.length;
		int[] result = new int[length + 1];
		System.arraycopy(a, 0, result, 0, length);
		result[length] = b;
		return result;
	}


	public static byte[] concatenate( byte[] a, byte[] b ) {
		if ( a != null && b != null ) {
			byte[] rv = new byte[a.length + b.length];

			System.arraycopy(a, 0, rv, 0, a.length);
			System.arraycopy(b, 0, rv, a.length, b.length);

			return rv;
		} else if ( b != null ) {
			return clone(b);
		} else {
			return clone(a);
		}
	}


	public static byte[] concatenate( byte[] a, byte[] b, byte[] c ) {
		if ( a != null && b != null && c != null ) {
			byte[] rv = new byte[a.length + b.length + c.length];

			System.arraycopy(a, 0, rv, 0, a.length);
			System.arraycopy(b, 0, rv, a.length, b.length);
			System.arraycopy(c, 0, rv, a.length + b.length, c.length);

			return rv;
		} else if ( b == null ) {
			return concatenate(a, c);
		} else {
			return concatenate(a, b);
		}
	}


	public static byte[] concatenate( byte[] a, byte[] b, byte[] c, byte[] d ) {
		if ( a != null && b != null && c != null && d != null ) {
			byte[] rv = new byte[a.length + b.length + c.length + d.length];

			System.arraycopy(a, 0, rv, 0, a.length);
			System.arraycopy(b, 0, rv, a.length, b.length);
			System.arraycopy(c, 0, rv, a.length + b.length, c.length);
			System.arraycopy(d, 0, rv, a.length + b.length + c.length, d.length);

			return rv;
		} else if ( d == null ) {
			return concatenate(a, b, c);
		} else if ( c == null ) {
			return concatenate(a, b, d);
		} else if ( b == null ) {
			return concatenate(a, c, d);
		} else {
			return concatenate(b, c, d);
		}
	}


	public static byte[] prepend( byte[] a, byte b ) {
		if ( a == null ) {
			return new byte[ ] { b };
		}

		int length = a.length;
		byte[] result = new byte[length + 1];
		System.arraycopy(a, 0, result, 1, length);
		result[0] = b;
		return result;
	}
}
